Skip to content

feat: assorted improvements for CLI/daemon to be resilient and ergonomic#72

Merged
georgeh0 merged 7 commits intomainfrom
g/daemon-cli
Mar 16, 2026
Merged

feat: assorted improvements for CLI/daemon to be resilient and ergonomic#72
georgeh0 merged 7 commits intomainfrom
g/daemon-cli

Conversation

@georgeh0
Copy link
Copy Markdown
Member

@georgeh0 georgeh0 commented Mar 16, 2026

#52

georgeh0 and others added 5 commits March 15, 2026 21:56
Three root causes fixed:

1. Path resolution mismatch on Windows: init and auto_init_project used
   unresolved Path.cwd() but find_parent_with_marker/find_project_root
   resolve internally, causing comparison failures and daemon key mismatches.

2. LMDB not released on remove_project: The daemon ProjectRegistry
   dropped the Project from dicts without closing its SQLite connection or
   forcing GC of the Rust LMDB environment. On free-threaded Python (3.14t)
   and Windows, deferred GC kept the LMDB open, causing environment already
   open errors and PermissionErrors when deleting db files.

3. Silent connection close on streaming errors: When update_index async
   iteration failed in the daemon, the connection was closed without sending
   an ErrorResponse, causing the client to get an unhelpful EOFError.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lter

On 3.14t (free-threaded Python), gc.collect() alone does not release the
Rust LMDB environment because deferred reference counting keeps
core.Environment alive through App._core_env_app, ContextProvider._core_env,
and Environment._core_env.  Explicitly null these internal references in
Project.close() before gc.collect() so the Rust object is freed promptly.

Also resolve Path.cwd() in resolve_default_path() — on Windows, the
unresolved cwd did not match the resolved project_root, causing
relative_to() to fail and the subdirectory path filter to be skipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert fragile internal attribute clearing. On free-threaded Python, the
first gc.collect() frees Python wrappers whose Rust Drop implementations
issue further deferred Py_DECREF calls on core.Environment; a second
gc.collect() flushes those and actually drops the LMDB handle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On 3.14t, deferred reference counting means the Rust LMDB environment
is not released immediately after remove_project + gc.collect(). Add a
1-second sleep in the two tests that reset and re-index, giving the
runtime time to process pending deferred Py_DECREF calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of sleeping, restart the daemon after reset in the two tests
that re-index after removing databases. This reliably releases the LMDB
environment on all platforms including free-threaded Python (3.14t).

Also increase _wait_for_daemon timeout from 5s to 10s — Windows CI
runners occasionally need longer to start the daemon subprocess.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@georgeh0 georgeh0 merged commit 0b30f16 into main Mar 16, 2026
5 checks passed
@georgeh0 georgeh0 deleted the g/daemon-cli branch March 16, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant